package org.example.security;

import org.example.utils.KeysUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;

import java.util.Set;

/**
 * 授权管理
 */
@Configuration
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private static final Logger log = LoggerFactory.getLogger(AuthorizationManager.class);

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 授权管理 校验
     * @param mono 这里存放的是角色，既认证信息
     * @param authorizationContext  授权上下文
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        ServerHttpRequest httpRequest =  authorizationContext.getExchange().getRequest();

        String path =   httpRequest.getURI().getPath();
        log.info("path = {}",path);

        PathMatcher pathMatcher = new AntPathMatcher();
        // 对应跨域的预检请求直接放行
        if(httpRequest.getMethod() == HttpMethod.OPTIONS){
            //访问决策通过
            return Mono.just(new AuthorizationDecision(true));
        }
        // 对无需鉴权的应用 直接放行
        if(pathMatcher.match(KeysUtils.ADMIN_URL_PATTERN,path)){
            log.info("默认放行路径，path={}", path);
            return Mono.just(new AuthorizationDecision(true));
        }
        //获取Token
        String token = httpRequest.getHeaders().getFirst(KeysUtils.AUTHORIZATION_KEY);
        if(StringUtils.isEmpty(token)){
            log.info("请求token为空拒绝访问，path={}", path);
            return Mono.just(new AuthorizationDecision(false));
        }

        redisTemplate.opsForSet().add(path,"RW","ROLE");
        //从缓存中获取 Path，对应角色
        Set<Object> permissionRoles = redisTemplate.opsForSet().members(path);
        log.info("角色：{}",permissionRoles);



        //进行角色比对，比对成功则放行
        Mono<AuthorizationDecision> authorizationDecisionMono = mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(roleId -> {
                    // roleId是请求用户的角色(格式:ROLE_{roleId})，authorities是请求资源所需要角色的集合
                    log.info("访问路径：{}", path);
                    log.info("用户角色信息：{}", roleId);
                    log.info("资源需要权限authorities：{}", permissionRoles);
                    return permissionRoles.contains(roleId);
                })
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
        return authorizationDecisionMono;
    }

}
